import * as THREE from "three";
import {
    GLTFLoader
} from "three/examples/jsm/loaders/GLTFLoader";
import {
    ref,
    reactive,
    toRefs
} from "vue";
import {
    OrbitControls
} from "three/examples/jsm/controls/OrbitControls.js";
export default class ThreeUtil {
    /**
     * 配置变量
     */
    defaultMap: any;
    modelUrl: string;
    map: any;
    x: number;
    y: number;
    z: number;
    fov: number;
    aspect: number;
    near: number;
    far: number;
    loader: any;
    isLoading: any;
    loadingWidth: any;
    /**
     * Three变量
     */
    static scene:any;
    static renderer:any;
    static camera:any;
    static controls:any;
    /**
     * 
     * @param {Object} obj 
     * {
     *    camera:{
     *          x,y,z,fov,aspect,near,far
     *    },
     *    modelUrl:“”
     * }
     */
    constructor(obj:any) {
        this.defaultMap = {
            x: obj?.camera?.x ? obj.camera.x : 0,
            y: obj?.camera?.y ? obj.camera.y : 0,
            z: obj?.camera?.z ? obj.camera.z : 2,
        }; // 相机的默认坐标
        this.modelUrl = obj.modelUrl;
        this.map = reactive(this.defaultMap); //把相机坐标设置成可观察对象
        const {
            x,
            y,
            z
        } = toRefs(this.map); //输出坐标给模板使用
        this.x = x;
        this.y = y;
        this.z = z;
        /**
         * 相机相关配置，默认使用透视相机
         */
        this.fov = obj?.camera?.fov ? obj.camera.fov : 75;
        this.aspect = obj?.camera?.aspect ? obj.camera.aspect : innerWidth / innerHeight;
        this.near = obj?.camera?.near ? obj.camera.near : 1;
        this.far = obj?.camera?.far ? obj.camera.far : 10;
        this.loader = new GLTFLoader(); //引入模型的loader实例
        /**
         * 效果相关
         */
        this.isLoading = ref(true); //是否显示loading  这个load模型监听的进度
        this.loadingWidth = ref(0); // loading的进度
    }

    // 创建场景
    setScene = () => {
        ThreeUtil.scene = new THREE.Scene();
        ThreeUtil.renderer = new THREE.WebGLRenderer();
        ThreeUtil.renderer.setSize(innerWidth, innerHeight);
        document.body.appendChild(ThreeUtil.renderer.domElement);
    };
    // 创建相机
    setCamera = () => {
        const {
            x,
            y,
            z
        } = this.defaultMap;
        ThreeUtil.camera = new THREE.PerspectiveCamera(this.fov, this.aspect, this.near, this.far);
        ThreeUtil.camera.position.set(x, y, z);
    };
    //加载模型
    loadFile = (url:string) => {
        return new Promise((resolve, reject) => {
            this.loader.load(
                url,
                (gltf) => {
                    resolve(gltf);
                },
                ({
                    loaded,
                    total
                }) => {
                    let load = Math.abs((loaded / total) * 100);
                    this.loadingWidth.value = load;
                    if (load >= 100) {
                        setTimeout(() => {
                            this.isLoading.value = false;
                        }, 1000);
                    }
                    console.log((loaded / total) * 100 + "% loaded");
                },
                (err) => {
                    reject(err);
                }
            );
        });
    };

    // 创建灯光
    setLight = () => {
        let directionalLight:any = new THREE.DirectionalLight(0xffffff, 0.9);
        directionalLight.position.set(-4, 8, 4);
        let hemisphereLight:any = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.8);
        hemisphereLight.position.set(0, 8, 0);
        ThreeUtil.scene.add(directionalLight);
        ThreeUtil.scene.add(hemisphereLight);
        /**
         * 创建helper  辅助线
         */
        // const dhelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000);
        // const hHelper = new THREE.HemisphereLightHelper(hemisphereLight, 5);
        // ThreeUtil.scene.add(dhelper);
        // ThreeUtil.scene.add(hHelper);


        // const light = new THREE.HemisphereLight(0xffffff, 0xDC143C, 1);
        // const helper2 = new THREE.HemisphereLightHelper(light, 5);
        // ThreeUtil.scene.add(helper2,light);

        // const gridHelper = new THREE.GridHelper(10, 10, 0xFFFFFF, 0xFFFFFF);
        // ThreeUtil.scene.add(gridHelper);

        // const helper = new THREE.PolarGridHelper(10, 16, 8, 64);
        // ThreeUtil.scene.add(helper);
    };

    // 模型控制器
    setControls = () => {
        ThreeUtil.controls = new OrbitControls(ThreeUtil.camera, ThreeUtil.renderer.domElement);
        //   controls.maxPolarAngle = (0.9 * Math.PI) / 2;
        ThreeUtil.controls.enableZoom = true;
        ThreeUtil.controls.addEventListener("change", this.render);
    };
    //返回坐标信息
    render = () => {
        this.map.x = Number.parseInt(ThreeUtil.camera.position.x);
        this.map.y = Number.parseInt(ThreeUtil.camera.position.y);
        this.map.z = Number.parseInt(ThreeUtil.camera.position.z);
    };

    /**
     * 设置模型颜色
     * @param {string} color 
     * @param {string} useKitField 使用模型的套件字段
     */
    setModelColor = (color:string, useKitField:string) => {
        const currentColor = new THREE.Color(color);
        ThreeUtil.scene.traverse((child) => {
            // console.log('child.name：'+ child.name);
            if (child.isMesh) {
                console.log(child.name); //当前的贴图套件字段
                if (child.name.includes(useKitField)) {
                    // 设置贴图的颜色
                    child.material.color.set(currentColor);
                }
            }
        });
    };

    /**
     * 是否自动转动
     * @param {Boolean} rotation 是否自动旋转
     * @param {Float} speed 旋转速度
     * @returns 
     */
    autoRotation = (rotation = true, speed = 2) => {
        if (rotation == ThreeUtil.controls.autoRotate) return;
        ThreeUtil.controls.autoRotateSpeed = speed;
        ThreeUtil.controls.autoRotate = rotation;
    };

    // 循环场景 、相机、 位置更新
    loop = () => {
        requestAnimationFrame(this.loop);
        ThreeUtil.renderer.render(ThreeUtil.scene, ThreeUtil.camera);
        ThreeUtil.controls.update();
    };

    // 初始化函数
    /**
     * 
     * @param {*} obj 可以通过传true来不去初始化其对应的实例，因为你可以通过本类的实例去自定义它们
     */
    init = async obj => {
        this.setScene();
        !obj?.setCamera && this.setCamera();
        !obj?.setLight && this.setLight();
        !obj?.setControls && this.setControls();
        const gltf:any = await this.loadFile(this.modelUrl);
        ThreeUtil.scene.add(gltf.scene);
        this.loop();
    };
}